#include "hook.h"
#include "fdmanager.h"
#define HOOK_FUN(XX) \
    XX(sleep)        \
    XX(usleep)       \
    XX(nanosleep)    \
    XX(socket)       \
    XX(connect)      \
    XX(accept)       \
    XX(read)         \
    XX(readv)        \
    XX(recv)         \
    XX(recvfrom)     \
    XX(recvmsg)      \
    XX(write)        \
    XX(writev)       \
    XX(send)         \
    XX(sendto)       \
    XX(sendmsg)      \
    XX(close)        \
    XX(fcntl)        \
    XX(ioctl)        \
    XX(getsockopt)   \
    XX(setsockopt)

namespace sylar
{
    // 使用线程局部变量，每个线程都会判断一下是否启用了钩子
    static thread_local bool _hook_enable = false;
    bool is_hook_enable()
    {
        return _hook_enable;
    }
    void set_hook_enable(bool flag)
    { // 设置当前线程的钩子功能是否启用
        _hook_enable = flag;
    }
    void init_hook()
    {
        static bool is_inited = false; // 包装init_hook只被调用一次
        if (is_inited)
        {
            return;
        }
        is_inited = true;
#define XX(name) name##_f = (name##_fun)dlsym(RTLD_NEXT, #name);
        HOOK_FUN(XX)
#undef XX
    }
    struct Hookiniter
    {
        Hookiniter()
        {
            init_hook();
        }
    };
    static Hookiniter _hook_initer; // 静态变量会在main函数执行前初始化
}
// namespace sylar end;
struct time_info
{
    int canceled = 0; // 标明定时器是否被取消
};

template <class OriginFunc, class... Args>
static ssize_t do_io(int fd, OriginFunc fun, const char *hook_fun_name, uint32_t event, int timeout_so, Args &&...args)
{
    // 判断全局钩子是否启用
    if (!sylar::_hook_enable)
    {
        // 未启用
        return fun(fd, std::forward<Args &&>(args)...);
    }
    // 获取相关fd的上下文信息
    std::shared_ptr<sylar::FdCtx> ctx = sylar::FdMgr::GetInstance()->get(fd);
    if (!ctx)
    {
        // 不存在则调用原始接口
        return fun(fd, std::forward<Args &&>(args)...);
    }
    if (ctx->isClosed())
    {
        //
        errno = EBADF; // 文件描述符已经关闭
        return -1;
    }
    // 如果文件描述符不是一个socket或者用户设置了非阻塞模式，则直接调用原始的I/O操作函数。
    // 只针对阻塞模式的socket相关的io进行hook替换
    if (!ctx->isSocket() || ctx->getUserNonblock())
    {
        return fun(fd, std::forward<Args>(args)...);
    }
    // 根据类型获取读或者写的超时时间
    uint64_t timeout = ctx->getTimeout(timeout_so);
    std::shared_ptr<time_info> tinfo(new time_info);
retry:
    ssize_t n = fun(fd, std::forward<Args &&>(args)...); // 这里的fd是非阻塞的,fdctx初始化时就设置为了非阻塞
    // 1. 处理「系统中断（EINTR）」：重试 I/O
    while (n == -1 && errno == EINTR)
    {
        n = fun(fd, std::forward<Args &&>(args)...);
    }
    // 处理「资源暂时不可用（EAGAIN）」：异步等待 + 协程挂起
    if (n == -1 && errno == EAGAIN)
    {
        // 初始化相关异步资源
        sylar::IOManager *iom = sylar::IOManager::GetThis();
        std::shared_ptr<sylar::Timer> timer;
        std::weak_ptr<time_info> winfo(tinfo);
        if (timeout != (uint64_t)-1) // 这行代码检查是否设置了超时时间。
        {
            timer = iom->addCondTimer(timeout, [winfo, fd, iom, event]()
            {
                auto t = winfo.lock();
                if(!t || t->canceled) 
                {
                    return;
                }
                t->canceled = ETIMEDOUT;
                // cancel this event and trigger once to return to this fiber
                iom->cancelEvent(fd, (sylar::IOManager::Event)(event)); }, winfo,false);
        }
        int rt = iom->addEvent(fd, (sylar::IOManager::Event)(event));
        if (-1 == rt)
        {
            std::cout << hook_fun_name << " addEvent(" << fd << ", " << event << ")";
            if (timer) // 如果 rt 为-1，说明 addEvent 失败。此时，会打印一条调试信息，并且因为添加事件失败所以要取消之前设置的定时器，避免误触发。
            {
                timer->cancel();
            }
            return -1;
        }
        else
        {
            sylar::Fiber::getthis()->yield(); // 添加成功后就切走

            // 此时resume回来了执行后续代码
            if (timer)
            {
                timer->cancel(); // 取消定时器
            }
            // 接下来检查 tinfo->cancelled 是否等于 ETIMEDOUT。如果等于，说明该操作因超时而被取消，因此设置 errno 为 ETIMEDOUT 并返回 -1，表示操作失败。
            if (tinfo->canceled == ETIMEDOUT)
            {
                errno = tinfo->canceled;
                return -1;
            }
            // 如果没有超时，则跳转到 retry 标签，重新尝试这个操作。
            goto retry;
        }
    }
    return n;
}

extern "C"
{
    // declaration -> sleep_fun sleep_f = nullptr;
#define XX(name) name##_fun name##_f = nullptr;
    HOOK_FUN(XX)
#undef XX
    // only use at task fiber
    unsigned int sleep(unsigned int seconds)
    {
        if (!sylar::_hook_enable)
        {
            return sleep_f(seconds);
        }

        std::shared_ptr<sylar::Fiber> fiber = sylar::Fiber::getthis();
        sylar::IOManager *iom = sylar::IOManager::GetThis();
        // add a timer to reschedule this fiber
        iom->addTimer(seconds * 1000, [fiber, iom]()
                      { iom->push(fiber, -1); }, false);
        // wait for the next resume
        fiber->yield();
        return 0;
    }

    int usleep(useconds_t usec)
    {
        if (!sylar::_hook_enable)
        {
            return usleep_f(usec);
        }

        std::shared_ptr<sylar::Fiber> fiber = sylar::Fiber::getthis();
        sylar::IOManager *iom = sylar::IOManager::GetThis();
        // add a timer to reschedule this fiber
        iom->addTimer(usec / 1000, [fiber, iom]()
                      { iom->push(fiber); }, false);
        // wait for the next resume
        fiber->yield();
        return 0;
    }

    int nanosleep(const struct timespec *req, struct timespec *rem)
    {
        if (!sylar::_hook_enable)
        {
            return nanosleep_f(req, rem);
        }

        int timeout_ms = req->tv_sec * 1000 + req->tv_nsec / 1000 / 1000;

        std::shared_ptr<sylar::Fiber> fiber = sylar::Fiber::getthis();
        sylar::IOManager *iom = sylar::IOManager::GetThis();
        // add a timer to reschedule this fiber
        iom->addTimer(timeout_ms, [fiber, iom]()
                      { iom->push(fiber, -1); }, false);
        // wait for the next resume
        fiber->yield();
        return 0;
    }
    int socket(int domain, int type, int protocol)
    {
        if (!sylar::_hook_enable)
        {
            return socket_f(domain, type, protocol);
        }
        // 如果钩子启用了，则通过调用原始的 socket 函数创建套接字，并将返回的文件描述符存储在 fd 变量中。
        int fd = socket_f(domain, type, protocol);
        if (fd == -1) // fd是无效的情况
        {
            std::cerr << "socket() failed:" << strerror(errno) << std::endl;
            return fd;
        }
        // 如果socket创建成功会利用Fdmanager的文件描述符管理类来进行管理，判断是否在其管理的文件描述符中，如果不在扩展存储文件描述数组大小，并且利用FDctx进行初始化判断是是不是套接字，是不是系统非阻塞模式。
        sylar::FdMgr::GetInstance()->get(fd, true);
        return fd;
    }
    // 带超时管理的链接
    int connect_with_timeout(int fd, const struct sockaddr *addr, socklen_t addrlen, uint64_t timeout_ms)
    {
        if (!sylar::_hook_enable)
        {
            return connect_f(fd, addr, addrlen);
        }
        std::shared_ptr<sylar::FdCtx> ctx = sylar::FdMgr::GetInstance()->get(fd); // 根据文件描述符获取上下文
        // CTX不存在或者已经关闭则返回错误
        if (!ctx || ctx->isClosed())
        {
            errno = EBADF;
            return -1;
        }
        if (!ctx->isSocket())
        {
            return connect_f(fd, addr, addrlen);
        }
        if (ctx->getUserNonblock())
        {
            return connect_f(fd, addr, addrlen); // 设置了用户非阻塞,则执行原函数
        }
        int n = connect_f(fd, addr, addrlen); // 尝试connect操作
        if (n == 0)
        {
            return n; // 连接处理成功
        }
        if (n != -1 || errno != EINPROGRESS)
        {
            return n; // 连接处理失败 或者是 非资源未准备好情况 ---》真实错误情况
        }
        sylar::IOManager *iom = sylar::IOManager::GetThis(); // 获取当前线程的iomanager
        std::shared_ptr<sylar::Timer> timer;                 // 声明一个定时器对象。
        std::shared_ptr<time_info> tinfo(new time_info);     // 创建追踪定时器是否取消的对象
        std::weak_ptr<time_info> winfo(tinfo);               // 判断追踪定时器对象是否存在

        if (timeout_ms != (uint64_t)-1)
        { // 设置了超时时间
            timer = iom->addCondTimer(timeout_ms, [winfo, fd, iom]()
                                      {
                                          auto i = winfo.lock();
                                          if (!i || i->canceled)
                                          {
                                              return; // 定时器已取消
                                          }
                                          i->canceled = ETIMEDOUT;
                                          iom->cancelEvent(fd, sylar::IOManager::WRTIE); // 将指定的fd的事件触发将事件处理
                                      },
                                      winfo, false);
        }

        int ret = iom->addEvent(fd, sylar::IOManager::WRTIE); // 为文件描述符 fd 添加一个写事件监听器。这样的目的是为了上面的回调函数处理指定文件描述符
        if (ret == 0)
        {
            // 添加事件成功
            sylar::Fiber::getthis()->yield();

            // resume后
            if (timer) // 如果有定时器，取消定时器。
            {
                timer->cancel();
            }

            if (tinfo->canceled) // 发生超时错误或者用户取消
            {
                errno = tinfo->canceled; // 赋值给errno通过其查看具体错误原因。
                return -1;
            }
        }
        else
        {
            if (timer)
            {
                timer->cancel();
            }
            std::cerr << "connect addEvent(" << fd << ", WRITE) error";
        }
        // 验证非阻塞 socket 连接最终是否成功建立
        int error = 0;
        socklen_t len = sizeof(int);
        if (-1 == getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len)) // 通过getsocketopt检查套接字实际错误状态
                                                                      // 来判断是否成功或失败。
        {
            return -1;
        }
        if (!error) // 如果没有错误，返回 0 表示连接成功。
        {
            return 0;
        }
        else // 如果有错误，设置 errno 并返回错误。
        {
            errno = error;
            return -1;
        }
    }
    static uint64_t s_connect_timeout = -1; // s_connect_timeout 是一个 static 变量，表示默认的连接超时时间，类型为 uint64_t，可以存储 64 位无符号整数。//-1 通常用于表示一个无效或未设置的值。由于它是无符号整数，-1 实际上会被解释为 UINT64_MAX，表示没有超时限制。
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
    {
        return connect_with_timeout(sockfd, addr, addrlen, s_connect_timeout); // 调用hook启用后的connect_with_timeout函数
    }
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
    {
        int fd = do_io(sockfd, accept_f, "accept", sylar::IOManager::READ, SO_RCVTIMEO, addr, addrlen);
        if (fd >= 0)
        {
            sylar::FdMgr::GetInstance()->get(fd, true); // 添加到文件描述符管理器FdManager中
        }
        return fd;
    }
    // 处理iomanager的事件，在fdmanager中删除
    int close(int fd)
    {
        if (!sylar::_hook_enable)
        {
            return close_f(fd);
        }

        std::shared_ptr<sylar::FdCtx> ctx = sylar::FdMgr::GetInstance()->get(fd);

        if (ctx)
        {
            auto iom = sylar::IOManager::GetThis();
            if (iom)
            {
                iom->cancelAll(fd);
            }
            // del fdctx
            sylar::FdMgr::GetInstance()->del(fd);
        }
        return close_f(fd); // 处理完后调用原始系统调用
    }
    int ioctl(int fd, unsigned long request, ...)
    {
        va_list va;
        va_start(va, request);          // 让va变量指向request位置的下一个位置
        void *arg = va_arg(va, void *); // 将va参数以void形式存储正在arg中
        va_end(va);                     // 释放va变量

        if (request == FIONBIO)
        {
            bool user_nonblock = !!*(int *)arg;
            std::shared_ptr<sylar::FdCtx> ctx = sylar::FdMgr::GetInstance()->get(fd);
            // 检查获取的上下文对象是否有效（即 ctx 是否为空）。如果上下文对象无效、文件描述符已关闭或不是一个套接字，则直接调用原始的 ioctl 函数，返回处理结果。
            if (!ctx || ctx->isClosed() || !ctx->isSocket())
            {
                return ioctl_f(fd, request, arg);
            }
            // 如果上下文对象有效，调用其 setUserNonblock 方法，将非阻塞模式设置为 user_nonblock 指定的值。这将更新文件描述符的非阻塞状态。
            ctx->setUserNonblock(user_nonblock);
        }
        return ioctl_f(fd, request, arg);
    }
    int fcntl(int fd, int cmd, ...)
    {
        va_list va;
        va_start(va, cmd);
        switch (cmd)
        {
        case F_SETFL:
        { // 设置文件描述符的状态
            int arg = va_arg(va, int);
            va_end(va);
            std::shared_ptr<sylar::FdCtx> ctx = sylar::FdMgr::GetInstance()->get(fd);
            // 如果ctx无效，或者文件描述符关闭不是一个套接字就调用原始调用
            if (!ctx || ctx->isClosed() || !ctx->isSocket())
            {
                return fcntl_f(fd, cmd, arg);
            }
            // 用户是否设定了非阻塞
            ctx->setUserNonblock(arg & O_NONBLOCK);
            // 最后是否阻塞根据系统设置决定
            if (ctx->getSysNonblock())
            {
                arg |= O_NONBLOCK;
            }
            else
            {
                arg &= ~O_NONBLOCK;
            }
            return fcntl_f(fd, cmd, arg);
            break;
        }
        case F_GETFL:
        {
            va_end(va);
            int arg = fcntl_f(fd, cmd); // 调用原始的 fcntl 函数获取文件描述符的当前状态标志。
            std::shared_ptr<sylar::FdCtx> ctx = sylar::FdMgr::GetInstance()->get(fd);
            // 如果上下文无效、文件描述符已关闭或不是套接字，则直接返回状态标志。
            if (!ctx || ctx->isClosed() || !ctx->isSocket())
            {
                return arg;
            }
            // 这里是呈现给用户 显示的为用户设定的值
            // 但是底层还是根据系统设置决定的
            if (ctx->getUserNonblock())
            {
                return arg | O_NONBLOCK;
            }
            else
            {
                return arg & ~O_NONBLOCK;
            }
        }
        break;

        case F_DUPFD:
        case F_DUPFD_CLOEXEC:
        case F_SETFD:
        case F_SETOWN:
        case F_SETSIG:
        case F_SETLEASE:
        case F_NOTIFY:
#ifdef F_SETPIPE_SZ
        case F_SETPIPE_SZ:
#endif
        {
            int arg = va_arg(va, int);    // 从va获取标志位
            va_end(va);                   // 清理va
            return fcntl_f(fd, cmd, arg); // 调用原始调用
        }
        break;

        case F_GETFD:
        case F_GETOWN:
        case F_GETSIG:
        case F_GETLEASE:
#ifdef F_GETPIPE_SZ
        case F_GETPIPE_SZ:
#endif
        {
            va_end(va);              // 清理va变量
            return fcntl_f(fd, cmd); // 返回原始调用的结果
        }
        break;

        case F_SETLK:  // 设置文件锁，如果不能立即获得锁，则返回失败。
        case F_SETLKW: // 设置文件锁，且如果不能立即获得锁，则阻塞等待。
        // 获取文件锁的状态。如果文件描述符 fd 关联的文件已经被锁定，那么该命令会填充 flock 结构体，指示锁的状态。
        case F_GETLK:
        {
            // 从可变参数列表中获取 struct flock* 类型的指针，这个指针指向一个 flock 结构体，包含锁定操作相关的信息（如锁的类型、偏移量、锁的长度等）。
            struct flock *arg = va_arg(va, struct flock *);
            va_end(va);
            return fcntl_f(fd, cmd, arg);
        }
        break;

        case F_GETOWN_EX:                                                     // 获取文件描述符 fd 所属的所有者信息。这通常用于与信号处理相关的操作，尤其是在异步 I/O 操作中。
        case F_SETOWN_EX:                                                     // 设置文件描述符 fd 的所有者信息。
        {                                                                     // 和上面的思路类似
            struct f_owner_exlock *arg = va_arg(va, struct f_owner_exlock *); // 从可变参数中提取相应类型的结构体指针
            va_end(va);
            return fcntl_f(fd, cmd, arg);
        }
        break;
        default:
            va_end(va);
            return fcntl_f(fd, cmd);
        }
    }
    int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen)
    {
        return getsockopt_f(sockfd, level, optname, optval, optlen);
    }
    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen){
        if(!sylar::_hook_enable){
            return setsockopt_f(sockfd,level,optname,optval,optlen);
        }
        //如果 level 是 SOL_SOCKET 且 optname 是 SO_RCVTIMEO（接收超时）或 SO_SNDTIMEO（发送超时），代码会获取与该文件描述符关联的 FdCtx 上下文对象：
        if(level == SOL_SOCKET){
            if(optname == SO_RCVTIMEO || optname == SO_SNDTIMEO){
                std::shared_ptr<sylar::FdCtx> ctx = sylar::FdMgr::GetInstance()->get(sockfd);
                if(ctx){
                    const timeval* v = (const timeval*) optval;
                    ctx->setTimeout(optname,v->tv_sec*1000+v->tv_usec/1000);
                }
            }
        }
        //设置完信息后设置
        return setsockopt_f(sockfd,level,optname,optval,optlen);
    }
}
